package project;

import java.util.ArrayList;

import oracle.kv.DurabilityException;
import oracle.kv.FaultException;
import oracle.kv.KVStore;
import oracle.kv.Key;
import oracle.kv.Operation;
import oracle.kv.OperationExecutionException;
import oracle.kv.ReturnValueVersion;
import oracle.kv.Value;
import oracle.kv.ValueVersion;
import oracle.kv.Version;

/**
 * Classe de transaction, tous les manipulations sur KVstore sont ecrit ici.
 * */
public class Transaction {
	private KVStore[] stores;

	// Initialisation
	public Transaction(KVStore[] stores) {
		this.stores = stores;
	}

	/**
	 * Fonction T(i)
	 * 
	 * Realiser une transaction, dans la transaction, la quantite de produit i
	 * sera incremente par 1.
	 * 
	 * @param i
	 *            cle de produit
	 * @return Si la transaction reussit, retourne true, sinon, retourne false.
	 * */
	private boolean transaction(int i) {
		// Numero de l'index de store dans le matrix.
		int indexStore = hachage(i);
		Key key = Key.createKey("P" + i, "quality");
		ValueVersion valueVersion = this.stores[indexStore].get(key);
		Value value = valueVersion.getValue();
		Version version = valueVersion.getVersion();
		Integer quality = Integer.parseInt(new String(value.getValue()));
		quality++;
		// Les operation qui sont a la transaction
		ArrayList<Operation> operations = new ArrayList<Operation>();
		// Creer une operation
		Operation operation = this.stores[indexStore].getOperationFactory()
				.createPutIfVersion(key,
						Value.createValue(quality.toString().getBytes()),
						version, ReturnValueVersion.Choice.NONE, true);
		operations.add(operation);
		try {
			// Execute la transaction, si echoue, jeter une exception et
			// rollback.
			this.stores[indexStore].execute(operations);
			return true;
		} catch (DurabilityException e) {
			// echoue, jeter une exception et rollback.
			return false;
		} catch (OperationExecutionException e) {
			// echoue, jeter une exception et rollback.
			return false;
		} catch (FaultException e) {
			// echoue, jeter une exception et rollback.
			return false;
		}
	}

	/**
	 * Fonction de hachage. Determine Pi est stocker dans quel store.
	 * 
	 * @param i
	 *            index de produit
	 * @return index de store
	 * */
	private int hachage(int i) {
		int y = 0;
		if (i >= 0 && i <= 1000)
			y = (i - 1) / Init_1_a.storeSize;
		else
			y = -1;
		return y;
	}

	/**
	 * Obtenir le temps du systeme
	 * 
	 * @return le temps du systeme
	 * */
	public static long getSystemTime() {
		return System.currentTimeMillis();
	}

	/**
	 * Function A(i)
	 * 
	 * Calcule 1000 transaction sur un produit i.
	 * 
	 * @param i
	 *            cle de produit
	 * @return le temps d'attente
	 * */
	public long A(int i) {
		long start = getSystemTime();
		// Iterer 1000 fois
		for (int j = 1; j <= 1000; j++) {
			// Une transaction
			boolean isSuccessful = transaction(i);
			// Echoue, rollback
			if (!isSuccessful) {
				j--;
			}
		}
		
		long finish = getSystemTime();
		printResult(i);
		return (finish - start);
	}

	/**
	 * Afficher le resultat.
	 * 
	 * @param i
	 *            cle de produit
	 * 
	 * */
	public void printResult(int i) {
		Key k1 = Key.createKey("P" + i, "quality");
		int indexStore = hachage(i);
		ValueVersion valueVersion = this.stores[indexStore].get(k1);
		System.out.println(k1.toString() + " "
				+ new String(valueVersion.getValue().getValue()));
	}
}
